﻿using Core.Entities;
using Core.Enums;
using Core.Reader;
using Core.Tools;
using Core.Writer;
using System;
using System.Collections.Generic;

namespace Core.ReadWriter
{
    [Serializable]
    public abstract class BaseReadWriter : IDataReader, IDataWriter
    {
        private static readonly List<IReadWriterInitialization> Initializations;

        static BaseReadWriter()
        {
            Initializations = new List<IReadWriterInitialization>();

            AddInitialization(new DefaultInitialization());
        }

        /// <summary>
        /// 添加初始化器。在应用程序初始化时调用，不要再应用程序运行中调用，因为它没有异步锁。
        /// </summary>
        /// <param name="Initialization">自定义初始化器。需要考虑 Content 有值和无值两种情况。</param>
        public static void AddInitialization(ReadWriterInitializationDelegate Initialization)
        {
            if (Initialization == null)
            {
                throw new NullReferenceException("Initialization");
            }

            AddInitialization(new DelegateReadWriterInitialization(Initialization));
        }

        /// <summary>
        /// 添加初始化器。在应用程序初始化时调用，不要再应用程序运行中调用，因为它没有异步锁。
        /// </summary>
        /// <param name="Initialization">自定义初始化器。需要考虑 Content 有值和无值两种情况。</param>
        public static void AddInitialization(IReadWriterInitialization Initialization)
        {
            if (Initialization == null)
            {
                throw new NullReferenceException("Initialization");
            }

            Initializations.Add(Initialization);
        }

        public static BaseReadWriter Create(Type Type, RWSigns Sign = RWSigns.Property | RWSigns.ChangeType)
        {
            if (Type == null)
            {
                throw new NullReferenceException("Type");
            }

            for (int i = Initializations.Count - 1; i >= 0; --i)
            {
                var Result = Initializations[i].Initialization(Type, null, Sign);

                if (Result != null)
                {
                    return Result;
                }
            }

            throw new ArgumentException("Create reader failed.");
        }

        public static BaseReadWriter Create(object Content, RWSigns Sign = RWSigns.Property | RWSigns.ChangeType)
        {
            if (Content == null)
            {
                throw new NullReferenceException("Content");
            }

            var Type = Content.GetType();

            for (int i = Initializations.Count - 1; i >= 0; --i)
            {
                var Result = Initializations[i].Initialization(Type, Content, Sign);

                if (Result != null)
                {
                    return Result;
                }
            }

            throw new ArgumentException("Create reader failed.");
        }

        protected readonly RWSigns InternalSign;
        protected readonly object InternalContent;

        private int ReadFlag;
        private int NextFlag;

        public RWSigns Sign
        {
            get
            {
                return InternalSign;
            }
        }

        public virtual object Content
        {
            get
            {
                return InternalContent;
            }
        }

        public BaseReadWriter(object Content, RWSigns Sign)
        {
            if (Content == null)
            {
                throw new NullReferenceException("Content");
            }

            InternalContent = Content;
            InternalSign = Sign;
        }

        public abstract object this[string Name] { get; set; }

        public abstract object this[int Index] { get; set; }

        public abstract IEnumerable<string> Fields { get; }

        public abstract int Count { get; }

        public abstract RWTypes ObjectType { get; set; }

        public abstract void Add(string Name, object Value);

        public abstract bool TryGetValue(string Name, out object Value);

        public virtual IDataWriter CreateChildrenWriter(CreateChildrenWriterInfo Info)
        {
            if (Info.ObjectType != null)
            {
                switch (Info.ObjectType.Value)
                {
                    case RWTypes.Object:
                        return new DictionaryReadWriter(typeof(Dictionary<string, object>), new Dictionary<string, object>(), typeof(string), typeof(object), InternalSign);
                    case RWTypes.Array:
                        return new ListReadWriter(typeof(List<object>), new List<object>(), typeof(object), Sign);
                }
            }

            return new TableReadWriter(typeof(Table), new Table(), Sign);
        }

        public virtual void Dispose() { }

        public virtual void Next()
        {
            if (NextFlag == 1)
            {
                throw new IndexOutOfRangeException("Index");
            }

            ++NextFlag;
        }

        public virtual bool Read()
        {
            ++ReadFlag;

            return ReadFlag == 1;
        }

        public virtual void Reset()
        {
            ReadFlag = 0;
            NextFlag = 0;
        }

        internal object AsGetValue(object Value)
        {
            if (Value == null)
            {
                return null;
            }

            if (Value is IConvertible)
            {
                switch (((IConvertible)Value).GetTypeCode())
                {
                    case TypeCode.Boolean:
                    case TypeCode.Char:
                    case TypeCode.SByte:
                    case TypeCode.Byte:
                    case TypeCode.Int16:
                    case TypeCode.UInt16:
                    case TypeCode.Int32:
                    case TypeCode.UInt32:
                    case TypeCode.Int64:
                    case TypeCode.UInt64:
                    case TypeCode.Single:
                    case TypeCode.Double:
                    case TypeCode.Decimal:
                    case TypeCode.DateTime:
                    case TypeCode.String:
                        return Value;
                    case TypeCode.DBNull:
                        return null;
                }
            }

            if (Value is Guid)
            {
                return Value;
            }

            if (Value is IDataReader)
            {
                return Value;
            }

            return Create(Value, Sign);
        }

        internal object AsSetValue(object Value)
        {
            if (Value is BaseReadWriter)
            {
                return ((BaseReadWriter)Value).Content;
            }

            return Value;
        }
    }
}
